package com.jueyue.dao.aop;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.jdbc.core.CallableStatementCallback;
import org.springframework.jdbc.core.CallableStatementCreator;
import org.springframework.jdbc.core.ResultSetExtractor;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.core.simple.ParameterizedBeanPropertyRowMapper;

import com.jueyue.dao.annotation.query.Param;
import com.jueyue.dao.annotation.sql.SQL;
import com.jueyue.dao.format.SQLFormatter;
import com.jueyue.dao.pager.Pager;
import com.jueyue.dao.pager.dialect.DialectFactory;
import com.jueyue.dao.resource.SQLResource;
import com.jueyue.dao.spring.rowMapper.MiniColumnMapRowMapper;
import com.jueyue.dao.template.MiniDaoTemplate;
import com.jueyue.dao.util.ClassNameUtil;
import com.jueyue.dao.util.PublicUtil;

/**
 * DAO代理实现类
 * 
 * @author JueYue
 * @date 2014年1月12日
 * @version 1.0
 */
@SuppressWarnings({ "unchecked", "rawtypes" })
public class MiniDaoHandler implements MethodInterceptor {

	private static final Logger logger = LoggerFactory
			.getLogger(MiniDaoHandler.class);

	private MiniDaoTemplate template;

	private static final String INF_METHOD_ACTIVE = PublicUtil
			.getProperties("activeMethod");

	private static String INF_METHOD_BATCH = PublicUtil
			.getProperties("batchMethod");

	private boolean formatSql = false;

	private boolean showSql = false;

	public Object invoke(MethodInvocation methodInvocation) throws Throwable {
		Method method = methodInvocation.getMethod();
		Object[] args = methodInvocation.getArguments();
		// SQL模板
		String key = getKey(method);
		Map<String, Object> params = getSQL(key, method, args);
		// 返回结果
		Object returnObj = getValue(method, args, params);
		if (showSql) {
			String sql = formatSql ? SQLFormatter.format(params.get(
					ParamsVo.SQL).toString()) : params.get(ParamsVo.SQL)
					.toString();
			logger.info("key:" + key + "\nsql:" + sql);
		}
		return returnObj;
	}

	/**
	 * 获取值
	 * 
	 * @date 2014年1月12日
	 * @param method
	 * @param args
	 * @param templateSql
	 * @return
	 */
	private Object getValue(Method method, Object[] args,
			Map<String, Object> params) {
		// ——————————处理复杂操作，传入执行及回调
		if (params.containsKey(ParamsVo.CALLABLESTATEMENTCALLBACK)) {
			return template.execute((CallableStatementCreator) args[0],
					(CallableStatementCallback) args[1]);
		}
		String sql = params.get(ParamsVo.SQL).toString();
		// ——————————处理查询操作
		String methodName = method.getName();
		if (checkActiveKey(methodName)) {
			return template.update(sql);
		}
		if (checkBatchKey(methodName)) {
			return batchUpdate(sql);
		}
		// ————处理基础类型返回
		Class<?> returnType = method.getReturnType();
		if (returnType.isPrimitive()) {
			Number number = template.queryForObject(sql, BigDecimal.class);
			if ("int".equals(returnType.getSimpleName())) {
				return number.intValue();
			}
			if ("long".equals(returnType.getSimpleName())) {
				return number.longValue();
			}
			if ("double".equals(returnType.getSimpleName())) {
				return number.doubleValue();
			}
		}

		// 结果集解析
		if (params.containsKey(ParamsVo.RESULTSETEXTRACTOR)) {
			ResultSetExtractor<?> resultSetExtractor = (ResultSetExtractor<?>) params
					.get(ParamsVo.RESULTSETEXTRACTOR);
			return template.query(sql, resultSetExtractor);
		}

		// 行解析
		RowMapper<?> rowMapper = null;
		if (params.containsKey(ParamsVo.ROWMAPPER)) {
			rowMapper = (RowMapper<?>) params.get(ParamsVo.ROWMAPPER);
		}

		if (returnType.isAssignableFrom(List.class)) {
			returnType = getListRealType(method.getGenericReturnType()
					.toString(), rowMapper);
			RowMapper<?> rm = getListRowMapper(rowMapper, returnType);
			return template.query(sql, rm);
		}

		if (returnType.isAssignableFrom(Map.class)) {
			return template.queryForObject(sql,
					rowMapper == null ? new MiniColumnMapRowMapper()
							: rowMapper);
		}

		if (PublicUtil.isWrapClass(returnType)) {
			return template.queryForObject(sql, returnType);
		}

		RowMapper<?> rm = rowMapper == null ? ParameterizedBeanPropertyRowMapper
				.newInstance(returnType) : rowMapper;
		return template.queryForObject(sql, rm);
	}

	/**
	 * 获取真正的类型
	 * 
	 * @param genericReturnType
	 * @param rowMapper
	 * @return
	 */
	private Class<?> getListRealType(String genericReturnType,
			RowMapper<?> rowMapper) {
		String realType = genericReturnType.replace("java.util.List<", "")
				.replace(">", "");
		if (realType.contains("com.util.Map")) {
			rowMapper = new MiniColumnMapRowMapper();
		} else {
			try {
				return Class.forName(realType);
			} catch (ClassNotFoundException e) {
				e.printStackTrace();
			}
		}
		return null;
	}

	private RowMapper<?> getListRowMapper(RowMapper<?> rowMapper,
			Class<?> returnType) {
		RowMapper<?> rm = rowMapper == null ? ParameterizedBeanPropertyRowMapper
				.newInstance(returnType) : rowMapper;
		return rm;
	}

	/**
	 * 批处理
	 * 
	 * @Author JueYue
	 * @date 2013-11-17
	 * @return
	 */
	private int[] batchUpdate(String executeSql) {
		String[] sqls = executeSql.split(";");
		if (sqls.length < 100) {
			return template.batchUpdate(sqls);
		}
		int[] result = new int[sqls.length];
		List<String> sqlList = new ArrayList<String>();
		for (int i = 0; i < sqls.length; i++) {
			sqlList.add(sqls[i]);
			if (i % 100 == 0) {
				addResulArray(result, i + 1,
						template.batchUpdate(sqlList.toArray(new String[0])));
				sqlList.clear();
			}
		}
		addResulArray(result, sqls.length,
				template.batchUpdate(sqlList.toArray(new String[0])));
		return result;
	}

	/**
	 * 把批量处理的结果拼接起来
	 * 
	 * @Author JueYue
	 * @date 2013-11-17
	 */
	private void addResulArray(int[] result, int index, int[] arr) {
		int length = arr.length;
		for (int i = 0; i < length; i++) {
			result[index - length + i] = arr[i];
		}
	}

	/**
	 * 获得SQL
	 * 
	 * @date 2014年1月10日
	 * @param method
	 * @param args
	 *            参数顺序 map,pager,rowmapper
	 * @return
	 */
	private Map<String, Object> getSQL(String key, Method method, Object[] args) {
		addSQLByAnnotation(key, method);
		Map<String, Object> paramsMap = parseParams(method, args);
		if (!paramsMap.containsKey(ParamsVo.CALLABLESTATEMENTCREATOR)) {
			String sql = SQLResource.getSql(key,
					(Map<String, Object>) paramsMap.get(ParamsVo.ROOT));
			if (paramsMap.containsKey(ParamsVo.PAGER)) {
				sql = getPagerSQL((Pager) paramsMap.get(ParamsVo.PAGER), sql);
			}
			paramsMap.put(ParamsVo.SQL, sql);
		}
		return paramsMap;
	}

	/**
	 * 解析参数序列 2014年4月2日
	 * 
	 * @param method
	 * @param args
	 * @return
	 */
	private Map<String, Object> parseParams(Method method, Object[] args) {
		Map<String, Object> result = new HashMap<String, Object>();
		Param[] params = getParamAnnotations(method.getParameterAnnotations());
		// 组装SQL参数
		if (params != null) {
			Map<String, Object> map = new HashMap<String, Object>();
			for (int i = 0, alength = params.length; i < alength; i++) {
				map.put(params[i].value(), args[i]);
			}
			result.put(ParamsVo.ROOT, map);
		}
		// 识别特殊参数
		for (int i = params == null ? 0 : params.length; i < args.length; i++) {
			if (args[i] instanceof Map) {
				result.put(ParamsVo.ROOT, args[i]);
			} else if (args[i] instanceof Pager) {
				result.put(ParamsVo.PAGER, args[i]);
			} else if (args[i] instanceof RowMapper) {
				result.put(ParamsVo.ROWMAPPER, args[i]);
			} else if (args[i] instanceof CallableStatementCreator) {
				result.put(ParamsVo.CALLABLESTATEMENTCREATOR, args[i]);
			} else if (args[i] instanceof CallableStatementCallback) {
				result.put(ParamsVo.CALLABLESTATEMENTCALLBACK, args[i]);
			} else if (args[i] instanceof ResultSetExtractor) {
				result.put(ParamsVo.RESULTSETEXTRACTOR, args[i]);
			}
		}

		return result;
	}

	/**
	 * 获取注解参数 2014年4月2日
	 * 
	 * @param parameterAnnotations
	 * @return
	 */
	private Param[] getParamAnnotations(Annotation[][] parameterAnnotations) {
		if (parameterAnnotations.length == 0) {
			return null;
		}
		boolean ishave = false;
		for (int i = 0; i < parameterAnnotations[0].length; i++) {
			if (parameterAnnotations[0][i] instanceof Param) {
				ishave = true;
				break;

			}
		}
		if (ishave) {
			Param[] params = new Param[parameterAnnotations.length];
			for (int i = 0; i < parameterAnnotations.length; i++) {
				for (int j = 0; j < parameterAnnotations[i].length; j++) {
					if (parameterAnnotations[i][j] instanceof Param) {
						params[i] = (Param) parameterAnnotations[i][j];
						break;

					}
				}
			}
			return params;
		}
		return null;
	}

	/**
	 * 获取SQl 2014年4月2日 注解的SQL无法启动加载,只能运行时加载
	 * 
	 * @param method
	 * @param args
	 * @return
	 */
	private void addSQLByAnnotation(String key, Method method) {
		if (method.getAnnotation(SQL.class) == null) {
			return;
		}
		if (SQLResource.getLoader().findTemplateSource(key) == null) {
			SQLResource.getLoader().putTemplate(key,
					method.getAnnotation(SQL.class).value());
		}
	}

	private String getKey(Method method) {
		return ClassNameUtil.getInterRealName(method.getDeclaringClass()
				.getSimpleName()) + "." + method.getName();
	}

	/**
	 * 判斷是否是執行的方法（非查詢）
	 * 
	 * @param methodName
	 * @return
	 */
	private boolean checkActiveKey(String methodName) {
		String keys[] = INF_METHOD_ACTIVE.split(",");
		for (String s : keys) {
			if (methodName.startsWith(s))
				return true;
		}
		return false;
	}

	/**
	 * 判斷是否批處理
	 * 
	 * @param methodName
	 * @return
	 */
	private boolean checkBatchKey(String methodName) {
		String keys[] = INF_METHOD_BATCH.split(",");
		for (String s : keys) {
			if (methodName.startsWith(s))
				return true;
		}
		return false;
	}

	private String getPagerSQL(Pager page, String sql) {
		return DialectFactory.getSQLDialect().getPagerSql(page, sql);
	}

	public void setFormatSql(boolean formatSql) {
		this.formatSql = formatSql;
	}

	public void setShowSql(boolean showSql) {
		this.showSql = showSql;
	}

	public void setTemplate(MiniDaoTemplate template) {
		this.template = template;
	}
}
